﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Reflector;
using Reflector.CodeModel;

namespace Neovolve.Extensibility.Reflector.AssemblySorter
{
    /// <summary>
    /// The <see cref="SorterPackage"/>
    /// class is a Reflector addin that sorts the assemblies loaded in Reflector alphabetically.
    /// </summary>
    public class SorterPackage : IPackage
    {
        /// <summary>
        /// Loads the specified service provider.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        public void Load(IServiceProvider serviceProvider)
        {
            LaunchDebugger();

            // Get AssemblyManager reference for reordering
            AssemblyManager = (IAssemblyManager)serviceProvider.GetService(typeof(IAssemblyManager));
            AssemblyBrowser = (IAssemblyBrowser)serviceProvider.GetService(typeof(IAssemblyBrowser));

            SortAssemblies();

            AssemblyManager.AssemblyLoaded += AssemblyManager_AssemblyLoaded;
        }

        /// <summary>
        /// Launches the debugger.
        /// </summary>
        [Conditional("DEBUG")]
        private static void LaunchDebugger()
        {
            if (Debugger.IsAttached == false)
            {
                Debugger.Break();
            }
        }

        /// <summary>
        /// Unloads this instance.
        /// </summary>
        public void Unload()
        {
            AssemblyManager.AssemblyLoaded -= AssemblyManager_AssemblyLoaded;
        }

        /// <summary>
        /// Handles the AssemblyLoaded event of the AssemblyManager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AssemblyManager_AssemblyLoaded(object sender, EventArgs e)
        {
            SortAssemblies();
        }

        /// <summary>
        /// Sorts the assemblies.
        /// </summary>
        private void SortAssemblies()
        {
            if (SortInProgress)
            {
                return;
            }

            SortInProgress = true;

            SortedList<String, IAssembly> assemblies =
                new SortedList<string, IAssembly>(AssemblyManager.Assemblies.Count);

            // Loop through each assembly that is already loaded
            for (Int32 index = 0; index < AssemblyManager.Assemblies.Count; index++)
            {
                // Get the assembly by its index
                IAssembly assembly = AssemblyManager.Assemblies[index];

                // Add the assembly to the sorted list
                assemblies.Add(assembly.Name, assembly);
            }

            // Get the selected assembly
            IAssembly currentItem = AssemblyBrowser.ActiveItem as IAssembly;

            // Loop through the assemblies to find the first index that is out of order
            for (Int32 index = 0; index < assemblies.Count; index++)
            {
                // Get the sorted assembly and the loaded assembly for the index
                IAssembly sortedAssembly = assemblies.Values[index];
                Int32 loadedIndex = AssemblyManager.Assemblies.IndexOf(sortedAssembly);

                // Check if the assembly at this location matches
                if (index == loadedIndex)
                {
                    continue;
                }

                // The assembly at this index is out of order
                // Add and remove the assembly
                AssemblyManager.Assemblies.Remove(sortedAssembly);
                AssemblyManager.Assemblies.Add(sortedAssembly);
            }

            // Restore the current item
            AssemblyBrowser.ActiveItem = currentItem;

            // Mark the sort as completed
            SortInProgress = false;
        }

        /// <summary>
        /// Gets or sets the assembly browser.
        /// </summary>
        /// <value>The assembly browser.</value>
        private IAssemblyBrowser AssemblyBrowser
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the assembly manager.
        /// </summary>
        /// <value>The assembly manager.</value>
        private IAssemblyManager AssemblyManager
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether sorting is in progress.
        /// </summary>
        /// <value><c>true</c> if sorting is in progress; otherwise, <c>false</c>.</value>
        private Boolean SortInProgress
        {
            get;
            set;
        }
    }
}